const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

// 底层逻辑就是看x是不是一个promise？ 是promise就看这个promise是成功还是失败，如果不是直接调用resolve传递给p2
function resolvePromise(p2, x, resolve, reject) {
  // 用x来决定是走p2的 resolve 还是 reject
  // 而是要考虑我的promise和别人的promise 能兼容
  if(p2 === x){
    return reject(new TypeError('[TypeError: Chaining cycle detected for promise #<Promise>] MY'))
  }
  if((typeof x === 'object'  && x !== null) || typeof x === 'function'){
    let called = false
    try{
      let then = x.then;
      if(typeof then === 'function'){
        // 用法上一次获取到的then 来调用，而不是采用在此获取的方式
        then.call(x,(y)=>{
          if(called) return 
          called = true
          resolvePromise(p2, y, resolve, reject)
        },(r)=>{
          if(called) return 
          called = true
          reject(r)
        })
      }else{
        resolve(x); //  {a:1}
      }
    }catch(e){
      if(called) return 
      called = true
      reject(e);
    }
  }else{
    resolve(x)
  }
}
class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) => {

      if(value instanceof Promise){ // 只关系自己的promise
        return value.then(resolve,reject); // 如果是promise就不停的递归操作
      }
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled :data=>data
    onRejected = typeof onRejected === 'function' ? onRejected :err=>{throw err}
    let p2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // ...
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(p2, x, resolve, reject);
          } catch (e) {
            console.log(e);
            reject(e);
          }
        });
      }
      if (this.status === REJECTED) {
        // ...
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(p2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      }
      if (this.status === PENDING) {
        // 如果是pending状态，意味着没有更改状态
        this.onResolvedCallbacks.push(() => {
          // ...
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(p2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });
        this.onRejectedCallbacks.push(() => {
          // ...
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(p2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });
      }
    });

    return p2;
  }
  catch(onRejected){
    return this.then(null,onRejected)
  }
}
Promise.resolve = function(value){
  return new Promise((resolve,reject)=>{
    resolve(value)
  })
}
Promise.reject = function(error){
  return new Promise((resolve,reject)=>{
    reject(error)
  })
}
Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
      dfd.resolve = resolve;
      dfd.reject = reject;
  })
  return dfd;
}
Promise.all = function(promises){
  return new Promise((resolve,reject)=>{
      const results = [];
      let times = 0;
      promises.forEach((val,index) => {
          Promise.resolve(val).then(result=>{
              results[index] = result; // 保证顺序11对应
              if(++times === promises.length){
                  resolve(results)
              }
          },reject)
      });
  })
}

Promise.race = function(promises){
  return new Promise((resolve,reject)=>{
      const results = [];
      let times = 0;
      promises.forEach((val,index) => {
          Promise.resolve(val).then(resolve,reject)
      });
  })
}

module.exports = Promise;

// npm install promises-aplus-tests -g